/**
 * Interval.java
 *
 * Created on Feb 27, 2006 at 2:09:05 PM
 *
 * Copyright 2008 the Desert Labs Project (http://desertlabs.us)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package us.desertlabs.number;

/**
 * An instance of this class is used to represent a single range of integers
 * between a minimum and maximum value.
 *
 * @author Brandon Franklin
 */
public class Interval {

    /** The maximum value of the interval */
    final private int max;

    /** The minimum value of the interval */
    final private int min;

    /**
     * Constructs a new instance of <CODE>Interval</CODE>.
     *
     * @param min the minimum value
     * @param max the maximum value
     */
    public Interval( final int min, final int max ) {
        this.min = min;
        this.max = max;
    }

    /**
     * Returns true if and only if this <CODE>Interval</CODE> can be merged
     * with the provided one. For this to be true, the other interval must
     * either intersect this one or be consecutive with it.
     *
     * @param otherInt the <CODE>Interval</CODE> to test for mergability with
     *        this one
     * @return true if and only if this <CODE>Interval</CODE> can be merged
     *         with the provided one
     */
    public boolean canMergeWith( final Interval otherInt ) {
        if( intersects( otherInt ) ) {
            return true;
        }

        return isConsecutiveWith( otherInt );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals( final Object obj ) {
        if( obj instanceof Interval ) {
            final Interval otherInt = (Interval)obj;
            if( min != otherInt.min ) {
                return false;
            }

            return (max == otherInt.max);
        }

        throw new IllegalArgumentException( "Parameter must be an instance of Interval" );
    }

    /**
     * Returns the value of max.
     *
     * @return the value of max
     */
    public int getMax() {
        return max;
    }

    /**
     * Returns the value of min.
     *
     * @return the value of min
     */
    public int getMin() {
        return min;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        return min + max * 13;
    }

    /**
     * Returns true if and only if this <CODE>Interval</CODE> overlaps the
     * supplied one.
     *
     * @param otherInt the <CODE>Interval</CODE> to test for overlap with this
     *        one
     * @return true if and only if this <CODE>Interval</CODE> overlaps the
     *         supplied one
     */
    public boolean intersects( final Interval otherInt ) {

        // Is min within other interval?
        if( (min > otherInt.min) && (min < otherInt.max) ) {
            return true;
        }

        // Is max within other interval?
        if( (max > otherInt.min) && (max < otherInt.max) ) {
            return true;
        }

        // Is the other interval completely inside this one?
        if( (min < otherInt.min) && (max > otherInt.max) ) {
            return true;
        }

        return false;
    }

    /**
     * Returns true if and only if this <CODE>Interval</CODE> is consecutive
     * with the provided one, touching at either the maximum or minimum end.
     *
     * @param otherInt the <CODE>Interval</CODE> to test for consecutiveness
     *        with this one
     * @return true if and only if this <CODE>Interval</CODE> is consecutive
     *         with the provided one
     */
    public boolean isConsecutiveWith( final Interval otherInt ) {

        if( min - 1 == otherInt.max ) {
            return true;
        }

        if( max + 1 == otherInt.min ) {
            return true;
        }

        return false;
    }

    /**
     * Returns the <CODE>Interval</CODE> that results from merging this one
     * and the provided one. If the two <CODE>Interval</CODE>s cannot be
     * merged, throws an <CODE>IllegalArgumentException</CODE>.
     *
     * @param otherInt the <CODE>Interval</CODE> to merge with this one
     * @return the <CODE>Interval</CODE> that results from merging this one
     *         and the provided one
     * @throws IllegalArgumentException if the provided <CODE>Interval</CODE>
     *         cannot be merged with this one
     */
    public Interval merge( final Interval otherInt ) {
        if( !canMergeWith( otherInt ) ) {
            throw new IllegalArgumentException( "These Intervals cannot be merged" );
        }

        final int newMin = Math.min( min, otherInt.min );
        final int newMax = Math.max( max, otherInt.max );

        return new Interval( newMin, newMax );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return "[" + min + "-" + max + "]";
    }
}
